package benchmark;

import java.util.HashMap;

import utils.Logger;
import ilog.concert.IloException;
import ilog.cplex.IloCplex;
import cruncher.NaiveCruncher;
import cruncher.Solution;

public class BenchmarkRunner {
	final static String MPS_BIG_FILENAME="TSP.200.mps";
	final static String MPS_SMALL_FILENAME="TSP.10.mps";
	final static String IZUNIT="C:\\Users\\igorzar\\Documents\\My Docs\\Research\\Dropbox\\Cruncher\\izunit.mps";
	final static int RUNNING_TIME_SEC = 120*60;
	
	HashMap<Player,Double> scores = new HashMap<BenchmarkRunner.Player, Double>();
	HashMap<Player,Double> objValues = new HashMap<Player,Double>();

	public enum Player {
	    Cplex, CplexPolish, Cruncher 
	}
	Player[] players; //the participants in the benchmark
	private int runs;

	
	public BenchmarkRunner(int runs, Player[] players) {
		this.runs = runs;
		this.players = players;
		for (Player p : players)
		{
			scores.put(p,0.0);
		}
	}

	public static void main(String[] args) {
		Logger.enabled = true;
		Player[] players = {Player.Cruncher};
		BenchmarkRunner runner = new BenchmarkRunner(2,players);
		runner.runBenchmark(IZUNIT,RUNNING_TIME_SEC);

	}

	private void runBenchmark(String mpsFilename, int runningTimeSec) {
		
		Logger.INFO(String.format("Running benchmark for %d seconds", runningTimeSec));
		
		double objCplex = Double.NaN, objPolish = Double.NaN, objCruncher = Double.NaN;
		
		if (isParticipant(Player.Cplex))
		{
			objCplex = runCPLEX(mpsFilename,runningTimeSec);
			objValues.put(Player.Cplex, objCplex);
			Logger.INFO(String.format("CPLEX found solution with value %f\n\n\n", objCplex));
		}
		
		if (isParticipant(Player.CplexPolish))
		{
			objPolish = runCPLEXWithPolish(mpsFilename,runningTimeSec);
			objValues.put(Player.CplexPolish, objPolish);
			Logger.INFO(String.format("CPLEX with polish found solution with value %f\n\n\n", objPolish));
		}

		if (isParticipant(Player.Cruncher))
		{
		for (int i=0; i < runs; i++)
			{
				Logger.INFO("Running Cruncher experiment " + i);
				objCruncher = runCruncher(mpsFilename,runningTimeSec).getObjValue();
				objValues.put(Player.Cruncher, objCruncher);
				
				Logger.INFO(String.format("CPLEX: %f, Polish %f, Cruncher: %f\n\n\n",objCplex,objPolish,objCruncher));
				
				calculateRoundScores();
			}
		}
		Logger.INFO(String.format("Benchmark score on %s: ",mpsFilename));
		for (Player p : players)
		{
			Logger.INFO(String.format("%s:%.1f",p,scores.get(p)));
		}
		
	}

	private boolean isParticipant(Player p) {
		
		for(Player player : players)
		{
			if (p == player) return true;
		}
		return false;
	}

	private void calculateRoundScores() {
		//update scores from objValues
		//not efficient
		for (int i = 0; i < this.players.length - 1; i++)
		{
			Player player = players[i];
			for (int j = i + 1 ; j < this.players.length; j++)
			{
				Player opponent = players[j];
				
				if (objValues.get(player)==objValues.get(opponent))
				{
					scores.put(player, scores.get(player) + 0.5);
					scores.put(opponent, scores.get(opponent) + 0.5);
					
				}else if (objValues.get(player) < objValues.get(opponent))
				{
					scores.put(player, scores.get(player) + 1.0);
				}else
				{
					scores.put(opponent, scores.get(opponent) + 1.0);
				}
			}
		}
	
		
	}

	protected Solution runCruncher(String mpsFilename, int timeSec)
	{
		NaiveCruncher cruncher =  new NaiveCruncher();
		cruncher.readfile(mpsFilename);
		Solution solCruncher = cruncher.localSearch(timeSec);
		cruncher.cleanup();
		return solCruncher;
	}
	
	protected double runCPLEX(String mpsFilename, int timeSec)
	{
		double objCplex = Double.MAX_VALUE;
		IloCplex cplex = null;
		try {
			Logger.INFO("Running CPLEX");
			cplex = new IloCplex();
			cplex.setOut(null);
			cplex.importModel(mpsFilename);
			cplex.setParam(IloCplex.DoubleParam.TiLim, timeSec);
			if (!cplex.solve())
			{
				Logger.INFO(String.format("CPLEX didn't find any solution in %d seconds",timeSec));
				return Double.MAX_VALUE;
			}
			objCplex = cplex.getObjValue();
			cplex.end();
			
		} catch (IloException e) {
			e.printStackTrace();
		} finally {
			if (cplex != null) {cplex.end();}
		}
		return objCplex;
	}
	
	protected double runCPLEXWithPolish(String mpsFilename, int timeSec)
	{
		double objCplex = Double.MAX_VALUE;
		IloCplex cplex = null;
		try {
			Logger.INFO("Running CPLEX with polish");
			cplex = new IloCplex();
			cplex.setOut(null);
			cplex.importModel(mpsFilename);
			cplex.setParam(IloCplex.DoubleParam.TiLim, timeSec);
			cplex.setParam(IloCplex.DoubleParam.PolishAfterTime, timeSec/4);
			cplex.setParam(IloCplex.IntParam.PolishAfterIntSol, 3);
			if (!cplex.solve())
			{
				Logger.INFO(String.format("CPLEX didn't find any solution in %d seconds",timeSec));
				return Double.MAX_VALUE;
			}
			objCplex = cplex.getObjValue();
			cplex.end();
			
		} catch (IloException e) {
			e.printStackTrace();
		} finally {
			if (cplex != null) {cplex.end();}
		}
		return objCplex;
	}
}
